package com.example.utils;

import jakarta.annotation.Resource;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;

/**
 * 问题	                    解答
 * 是否会重复？	            不会，因为 Redis 的 INCR 是原子操作，哪怕多个线程并发调用，也一定唯一。
 * 是否有性能瓶颈？	        主要瓶颈是 Redis 网络延迟和并发连接数，使用本地 Redis 或连接池配置优化可以缓解。
 * 会不会出现阻塞？	        Redis 是单线程的，如果并发过高，确实会排队，但你这里 3 万次访问量属于中等压力。
 * Redis key 会变很大吗？	    不会，每天一个 key（比如 icr:order:2025:04:15），第二天自动换新 key。
 */
@Component
public class RedisIDWorker {

    /**
     * 开始时间戳
     */
    private static final long BEGIN_TIMESTAMP = 1640995200L;
    /**
     * 序列号的位数
     */
    private static final int COUNT_BITS = 32;

    private StringRedisTemplate stringRedisTemplate;

    public RedisIDWorker(StringRedisTemplate stringRedisTemplate) {
        this.stringRedisTemplate = stringRedisTemplate;
    }

    /**
     * 获取自增后的全局唯一ID
     * 利用 高 32 位存放秒级时间戳，低 32 位作为自增序号
     * 每秒最多能生成 2^32个不同的 ID
     * 每天使用 Redis 按照日期记录自增序列（即 count），不仅生成唯一 ID，也能顺带统计当天生成了多少个 ID
     *
     * @param keyPrefix 业务key前缀
     * @return 自增后的全局唯一ID
     */
    public long nextID(String keyPrefix) {
        // 1.生成时间戳
        LocalDateTime now = LocalDateTime.now();
        long nowSecond = now.toEpochSecond(ZoneOffset.UTC);
        long timeStamp = nowSecond - BEGIN_TIMESTAMP;
//        System.out.println("timeStamp = " + timeStamp);
        // 2.生成序列号
        // 2.1.获取当前日期，精确到天
        String date = now.format(DateTimeFormatter.ofPattern("yyyy:MM:dd"));
//        System.out.println("date = " + date);
        // 2.2.自增长
        /**
         * Redis 并不是用来存储最终的 ID，而是：
         * 记录当前这一天内已经生成了多少个 ID，实现自增计数。
         * Redis 在这里的作用 就是按天维护一个 key，自增记录当天产生了多少个 ID
         */
        String key = "icr:" + keyPrefix + ":" + date;
        long count = stringRedisTemplate.opsForValue().increment(key);
//        System.out.println("count = " + count);
        // 3.拼接并返回
        // 返回自增后的全局唯一ID，将timeStamp从最低位左移COUNT_BITS位，低COUNT_BITS位补0，再与count做或运算填充
        return timeStamp << COUNT_BITS | count;
    }

//    public static void main(String[] args) {
//        LocalDateTime time = LocalDateTime.of(2022, 1, 1, 0, 0);
//        long second = time.toEpochSecond(ZoneOffset.UTC);
//        System.out.println(second);
//    }

}
